const { Service } = require('egg');
const { sm4 } = require('sm-crypto');
const fs = require('fs');
const path = require('path');
const fse = require('fs-extra');
const fileRoot = '../config';
const user = '../code';
const nodemailer = require('nodemailer');
const stringRandom = require('string-random');
const svgCaptcha = require('svg-captcha');
const moment = require('moment');
class HomeService extends Service {

  async isStart() {
    console.log(
      '                   _oo0oo_                     \n' +
      '                  o8888888o										\n' +
      '                  88" . "88										\n' +
      '                  (| -_- |)										\n' +
      '                   O\\ = /O										\n' +
      "               ____/`---'\\____									\n" +
      "             .   ' \\\\| |// `.									\n" +
      '              / \\\\||| : |||// \\								\n' +
      '           / _||||| -卍- |||||- \\								\n' +
      '              | | \\\\\\ - /// | |								\n' +
      "            | \\_| ''\\---/'' | |								\n" +
      '             \\ .-\\__ `-` ___/-. /							\n' +
      "          ___`. .' /--.--\\ `. . __							\n" +
      '       ."" "< `.___\\_<|>_/___. ` >" "".        \n' +
      '      | | : `- \\`.;`\\ _ /`;.`/ - ` : | |       \n' +
      '        \\ \\ `-. \\_ __\\ /__ _/ .-` / /          \n' +
      "======`-.____`-.___\\_____/___.-`____.-'======  \n" +
      "                   `=---='                     \n" +
      '.............................................  \n\t\t' +
      '佛祖镇楼                  BUG辟易                          \n\t' +
      '佛曰:\n\t\t' +
      '写字楼里写字间，写字间里程序员；\n\t\t' +
      '程序人员写程序，又拿程序换酒钱。\n\t\t' +
      '酒醒只在网上坐，酒醉还来网下眠；\n\t\t' +
      '酒醉酒醒日复日，网上网下年复年。\n\t\t' +
      '但愿老死电脑间，不愿鞠躬老板前；\n\t\t' +
      '奔驰宝马贵者趣，公交自行程序员。\n\t\t' +
      '别人笑我忒疯癫，我笑自己命太贱；\n\t\t' +
      '不见满街漂亮妹，哪个归得程序员？\n\t\t' +
      '.............................................  \n\t\t' +
      ' ===================> 项目启动端口 ' + this.config.cluster.listen.port
    );
  }

  async isInit() {
    try {

      const file = path.join(user, 'email.txt');

      const is_exist = fs.existsSync(file);

      return !is_exist;
    } catch (error) {
      throw error;
    }
  }

  async sendCode(email, type) {
    try {
      const { errorString } = this.app.config;
      // 判断是否需要重新发送验证码
      const codeFile = path.join(user, type + '.txt');

      if (fs.existsSync(codeFile)) {
        throw new Error(errorString.codeSendRepeatError);
      }

      // 生成验证码
      const code = stringRandom(6, { numbers: true });

      // 发送验证码
      const transporter = nodemailer.createTransport(this.app.config.email);

      const content = {
        from: this.app.config.email.auth.user,
        to: email,
        subject: '用于初始化邮箱',
        html: `<b>${code}</b>`,
      };


      await transporter.sendMail(content);

      fse.ensureFileSync(codeFile);
      fs.writeFileSync(codeFile, code, 'utf-8');

      setTimeout(() => {
        fse.remove(codeFile);
      }, 5 * 60 * 1000);

      return;
    } catch (error) {
      throw error;
    }
  }

  // 系统初次启动需要配置邮箱账号, 当邮箱账号配置好之后, 则自动创建用户
  async createEmail(email, code) {
    const { errorString } = this.app.config;
    try {
      const codeFile = path.join(user, 'insert.txt');
      const file = path.join(user, 'email.txt');
      if (!fs.existsSync(codeFile)) {
        throw new Error(errorString.sendEmailCodeError);
      }

      if (fs.existsSync(file)) {
        throw new Error(errorString.repeatBindEmailError);
      }

      const origin = fs.readFileSync(codeFile, 'utf-8');

      if (code !== origin) {
        throw new Error(errorString.codeError);
      }


      let result;
      try {
        result = await this.createAdmin(file, email);
      } catch (error) {
        fse.remove(file);
        fse.remove(path.join(user, 'auth.txt'));
      }
      return result;
    } catch (error) {
      throw error;
    }
  }

  // 更新邮箱
  async updateEmail(old_email, new_email, code) {
    const { errorString } = this.app.config;
    try {
      const origin_file = path.join(user, 'email.txt');

      if (!fs.existsSync(origin_file)) {
        throw new Error(errorString.initSystem);
      }

      const origin_email = fs.readFileSync(origin_file, 'utf-8');

      if (old_email !== origin_email) {
        throw new Error(errorString.emailError);
      }

      const origin_code_file = path.join(user, 'updateEmail.txt');

      if (!fs.existsSync(origin_code_file)) {
        throw new Error(errorString.codeTimeError);
      }

      const origin_code = fs.readFileSync(origin_code_file);

      if (origin_code !== code) {
        throw new Error(errorString.codeError);
      }

      fs.writeFileSync(origin_file, new_email, 'utf-8');

      return;
    } catch (error) {
      throw error;
    }
  }

  // 生成管理员账户和密码
  async createAdmin(file, email) {
    try {
      const { sm } = this.app.config;

      const name = stringRandom(6);
      const password = stringRandom(16);

      const key = stringRandom(32);
      const iv = stringRandom(32);

      const target = sm4.encrypt(password, key, { mode: 'cbc', iv });

      let content = { name, password: target, key, iv };

      content = sm4.encrypt(JSON.stringify(content), sm.key, { mode: sm.mode, iv: sm.iv });

      fse.ensureFileSync(path.join(user, 'auth.txt'));
      fse.ensureFileSync(file);
      fs.writeFileSync(file, email, 'utf-8');
      fs.writeFileSync(path.join(user, 'auth.txt'), content, 'utf-8');

      return {
        name,
        password,
      };
    } catch (error) {
      throw error;
    }
  }


  async updatePassword(password, email, code) {
    const { sm, errorString } = this.app.config;
    try {
      if (!fs.existsSync(path.join(user, 'email.txt'))) {
        throw new Error(errorString.initSystem);
      }

      const origin_email = fs.readFileSync(path.join(user, 'email.txt'), 'utf-8');

      if (origin_email !== email) {
        throw new Error(errorString.emailError);
      }

      if (!fs.existsSync(path.join(user, 'update.txt'))) {
        throw new Error(errorString.codeSendError);
      }
      const origin_code = fs.readFileSync(path.join(user, 'update.txt'), 'utf-8');

      if (origin_code !== code) {
        throw new Error(errorString.codeError);
      }

      const origin_file = path.join(user, 'auth.txt');
      const origin_content_en = fs.readFileSync(origin_file, 'utf-8');
      const origin_content = JSON.parse(sm4.decrypt(origin_content_en, sm.key, { mode: sm.mode, iv: sm.iv }));

      const key = stringRandom(32);
      const iv = stringRandom(32);

      const new_password = sm4.encrypt(password, key, { mode: 'cbc', iv });

      origin_content.password = new_password;
      origin_content.key = key;
      origin_content.iv = iv;

      const content = sm4.encrypt(JSON.stringify(origin_content), sm.key, { mode: sm.mode, iv: sm.iv });

      fs.writeFileSync(origin_file, content, 'utf-8');

      return;

    } catch (error) {
      throw error;
    }
  }

  async createLoginCode() {
    try {

      const code = svgCaptcha.create({
        size: 4,
        ignoreChars: '',
        noise: 4,
        color: true,
        background: '#cc9966',
      });

      fse.ensureDirSync(user);

      const code_file_path = path.join(user, 'login.txt');

      if (fs.existsSync(code_file_path)) {
        fse.remove(code_file_path);
      }

      fs.writeFileSync(code_file_path, code.text, 'utf-8');

      return code;
    } catch (error) {
      throw error;
    }
  }

  async login(code, login_name, password) {
    const ctx = this.ctx;
    try {
      const { sm, errorString } = this.app.config;
      const code_file_path = path.join(user, 'login.txt');
      if (!fs.existsSync(code_file_path)) {
        throw new Error(errorString.notSendLoginCode);
      }

      const origin_code = fs.readFileSync(code_file_path, 'utf-8');

      if (code.toLocaleLowerCase() !== origin_code.toLocaleLowerCase()) {
        throw new Error(errorString.passOrCodeIsError);
      }

      const user_file_path = path.join(user, 'auth.txt');

      if (!fs.existsSync(user_file_path)) {
        throw new Error(errorString.initSystem);
      }

      const user_encrypt = fs.readFileSync(user_file_path, 'utf-8');

      const user_info = JSON.parse(sm4.decrypt(user_encrypt, sm.key, { mode: sm.mode, iv: sm.iv }));

      const encrypt_password = sm4.encrypt(password, user_info.key, { mode: 'cbc', iv: user_info.iv });

      if (encrypt_password !== user_info.password) {
        throw new Error(errorString.passOrCodeIsError);
      }

      const token = ctx.app.jwt.sign({ login_name }, this.app.config.jwt.secret);
      ctx.session = {
        token,
        exp: 3 * 60 * 1000,
        ins: moment().valueOf(),
      };
      return 'Bearer ' + token;
    } catch (error) {
      throw error;
    }
  }


  async logout() {
    try {
      this.ctx.session = null;
      return;
    } catch (error) {
      throw error;
    }
  }

  // 创建环境
  async createEnv(env) {
    try {
      const { errorString } = this.app.config;

      const dir = path.join(fileRoot, env);
      const dirExist = fs.existsSync(dir);

      if (dirExist) {
        throw new Error(errorString.repeatCreateEnv);
      }

      fse.ensureDirSync(dir);


      return { parent: '', value: env };
    } catch (error) {
      throw error;
    }
  }

  // 删除环境
  async deleteEnv(env) {
    try {
      const dir = path.join(fileRoot, env);

      fse.emptyDirSync(dir);

      fse.rmdirSync(dir);


      return 'success';
    } catch (error) {
      throw error;
    }
  }

  // 重命名环境
  async renameEnv(oldEnv, newEnv) {
    const { errorString } = this.app.config;
    try {

      const dirOld = path.join(fileRoot, oldEnv);

      if (!fs.existsSync(dirOld)) {
        throw new Error(errorString.notUpdateEnv);
      }

      const dirNew = path.join(fileRoot, newEnv);

      const dirNewExist = fs.existsSync(dirNew);

      if (dirNewExist) {
        throw new Error(errorString.repeatCreateEnv);
      }


      fse.moveSync(dirOld, dirNew);

      return { parent: '', value: newEnv };

    } catch (error) {

      throw error;

    }
  }

  async listEnv() {
    try {
      const envDir = path.join(fileRoot);
      let list = [];
      if (fs.existsSync(envDir)) {
        list = fs.readdirSync(envDir);
      }

      return list;

    } catch (error) {
      throw error;
    }
  }

  // 创建应用
  async createApp(app, env) {
    const { errorString } = this.app.config;
    try {

      const envDir = path.join(fileRoot, env);

      const envDirExist = fs.existsSync(envDir);

      if (!envDirExist) {
        throw new Error(errorString.notCreateEnv);
      }

      const dir = path.join(fileRoot, env, app);
      const dirExist = fs.existsSync(dir);

      if (dirExist) {
        throw new Error(errorString.repeatCreateApp);
      }

      fse.ensureDirSync(dir);

      return { parent: env, value: app };
    } catch (error) {
      throw error;
    }
  }

  // 删除应用
  async deleteApp(app, env) {
    try {
      const dir = path.join(fileRoot, env, app);
      fse.emptyDirSync(dir);
      fse.rmdirSync(dir);
      return 'success';
    } catch (error) {
      throw error;
    }
  }

  // 应用重命名
  async renameApp(oldApp, newApp, env) {
    const { errorString } = this.app.config;
    try {
      const envDir = path.join(fileRoot, env);

      const envDirExist = fs.existsSync(envDir);

      if (!envDirExist) {
        throw new Error(errorString.notCreateEnv);
      }

      const old_dir = path.join(fileRoot, env, oldApp);

      if (!fs.existsSync(old_dir)) throw new Error(errorString.notCreateApp);

      const newDir = path.join(fileRoot, env, newApp);

      const newDirExist = fs.existsSync(newDir);

      if (newDirExist) {
        throw new Error(errorString.repeatUpdateApp);
      }

      const oldDir = path.join(fileRoot, env, oldApp);

      fse.moveSync(oldDir, newDir);

      return { parent: env, value: newApp };
    } catch (error) {
      throw error;
    }
  }

  // 应用列表

  async listApp(env) {
    try {
      let list = [];
      const envDir = path.join(fileRoot, env);

      const envDirExist = fs.existsSync(envDir);

      if (envDirExist) {
        list = fs.readdirSync(envDir);
      }

      return list;
    } catch (error) {
      throw error;
    }
  }

  // 创建应用配置
  async createAppConfig(app, env, config) {
    const { errorString, sm } = this.app.config;
    try {

      const env_dir = path.join(fileRoot, env);
      if (!fs.existsSync(env_dir)) throw new Error(errorString.notCreateEnv);

      const dir = path.join(fileRoot, env, app);
      const dirExist = fs.existsSync(dir);
      if (!dirExist) {
        throw new Error(errorString.notCreateApp);
      }

      if (fs.existsSync(path.join(dir, 'config.txt'))) throw new Error(errorString.repeatCreateConfig);
      const txt = JSON.stringify(config);

      const encryptData = sm4.encrypt(txt, sm.key, { mode: sm.mode, iv: sm.iv });
      fse.ensureFileSync(path.join(dir, 'config.txt'));
      fs.writeFileSync(path.join(dir, 'config.txt'), encryptData, 'utf-8');
      return config;
    } catch (error) {
      throw error;
    }
  }

  // 更新应用配置
  async updateAppConfig(app, env, config) {
    const { errorString, sm } = this.app.config;
    try {

      const env_dir = path.join(fileRoot, env);
      if (!fs.existsSync(env_dir)) throw new Error(errorString.notCreateEnv);

      const dir = path.join(fileRoot, env, app);
      const dirExist = fs.existsSync(dir);
      if (!dirExist) {
        throw new Error(errorString.notCreateApp);
      }

      if (!fs.existsSync(path.join(dir, 'config.txt'))) throw new Error(errorString.notExistConfig);

      const txt = JSON.stringify(config);

      const encryptData = sm4.encrypt(txt, sm.key, { mode: sm.mode, iv: sm.iv });

      fse.ensureFileSync(path.join(dir, 'config.txt'));
      fs.writeFileSync(path.join(dir, 'config.txt'), encryptData, 'utf-8');

      return config;
    } catch (error) {
      throw error;
    }
  }

  async deleteAppConfig(app, env) {
    const { errorString } = this.app.config;
    try {

      const env_dir = path.join(fileRoot, env);
      if (!fs.existsSync(env_dir)) throw new Error(errorString.notCreateEnv);

      const dir = path.join(fileRoot, env, app);
      const dirExist = fs.existsSync(dir);
      if (!dirExist) {
        throw new Error(errorString.notCreateApp);
      }
      if (!fs.existsSync(path.join(dir, 'config.txt'))) throw new Error(errorString.notExistConfig);

      fs.unlinkSync(path.join(dir, 'config.txt'));

      return;
    } catch (error) {
      throw error;
    }
  }

  // 查询列表
  async getList(env) {
    try {
      const dir = path.join(fileRoot, env);
      const list = fs.readdirSync(dir);
      const data = [];
      for (const l of list) {
        data.push({ parent: env, value: l });
      }
      return data;
    } catch (error) {
      throw error;
    }
  }

  // 查询配置详情
  async getInfo(app, env) {
    const { sm, errorString } = this.app.config;
    try {


      const env_dir = path.join(fileRoot, env);
      if (!fs.existsSync(env_dir)) throw new Error(errorString.notCreateEnv);

      const dir = path.join(fileRoot, env, app);
      const dirExist = fs.existsSync(dir);
      if (!dirExist) {
        throw new Error(errorString.notCreateApp);
      }

      if (!fs.existsSync(path.join(dir, 'config.txt'))) throw new Error(errorString.notExistConfig);

      const file = path.join(fileRoot, env, app, 'config.txt');
      const encryptData = fs.readFileSync(file, 'utf-8');

      const decryptData = sm4.decrypt(encryptData, sm.key, { mode: sm.mode, iv: sm.iv });
      return { config: JSON.parse(decryptData) };
    } catch (error) {
      throw error;
    }
  }

  // 获取sm配置信息
  async getSM() {
    const { sm } = this.app.config;
    try {
      const data = sm;
      return { data };
    } catch (error) {
      throw error;
    }
  }

  // app获取配置信息
  async appGetInfo(app, env) {
    try {
      const file = path.join(fileRoot, env, app, 'config.txt');

      const data = fs.readFileSync(file, 'utf-8');

      return { data };
    } catch (error) {
      throw error;
    }
  }

  // 健康检查

  async healthCheck(info) {
    try {
      if (!this.app.online) {
        this.app.online = {};
      }


      console.log(JSON.stringify(info));

      if (typeof info === 'object') {
        this.app.online[info.app] = {
          time: moment().format('YYYY-MM-DD HH:mm:ss'),
          status: 1,
        };
      }

      return true;
    } catch (error) {
      throw error;
    }
  }
}


module.exports = HomeService;
